/* Tagman
 * Author: Markus Fuchs
 * 
 * Widget to display and edit tags
 * has to be applied on a <input type="text">
 *
 * TODO
 *   - languages (add flag to tag and change lang on click on tag (option preferred language)
 *   - positioning of input field is terrible if we have multiple lines of tags
 *   - if addOnly true make own created tags editable
 *   - if editable is set to false after creation, remove delete buttons and event handler for input fields
 * MAYBE?:
 *   - When pressing tab in input field => apply tag and create&focus new input field
 *      
 */

(function($) {

	$.widget("wtf.tagman", {

		// These options will be used as defaults

		options : {
			editable: false,
			addOnly: false,
			tagSeparator: ',', // must be a single char!
			langDisable: true, // TODO not implemented
			langPreferred: "en" // TODO not implemented
		},

		// Set up the widget
		_create : function() {
			this._submitbtns = new Array();
			
			var self = this;
			this.element.parents("form").find("input[type=submit]").each(function(index, data) {
				self._submitbtns.push(data);
			});
			
			this._elemCount = 0;
			
			// insert widget content and replace text field
			$(this.element).hide();
			
			this._tagContainer = $("<div class=\"wtf-tagman-container\"></div>");
			this._tagHolder = $("<ul class=\"wtf-tagman-holder\"></ul>");
			this._tagInput = $("<input type=\"text\" size=\"1\" class=\"wtf-tagman-input\">"); // TODO add css attributes from source text field
			
			this.element.after(this._tagContainer);
			this._tagContainer.append(this._tagHolder);
			this._tagContainer.append(this._tagInput);
			this._tagInput.focus();
			
			// click on container => focus input field
			this._tagContainer.bind("click", {infield: this._tagInput}, function(event) {
				event.data.infield.focus();
			});
			
			// add tags from source text field
			$.each($(this.element).val().split(this.options.tagSeparator), function(index, data) {
				var tag = $.trim(data);
				if(tag != undefined && tag != "") {
					self.addItem(tag);
				}
			});
			
			// event handler for tag input field
			var fun1 = function(event) {
				var tag = event.data.widget._tagInput.val().split(',')[0];
				
				if($.trim($(event.target).val()) != "") {
					var oldTag = event.data.widget._getTag(tag);
					if(oldTag != undefined) {
						oldTag.effect("pulsate", { times:3 }, 200);
					} else {
						event.data.widget.addItem(tag);
					}
					event.data.widget._tagInput.val("");
					event.data.widget._tagInput.size("1");
				}
			};
			this._tagInput.bind("keypress", {widget: this}, function(event) {
				// 44 == ',' ; 13 == 'Enter'
				if(event.charCode === event.data.widget.options.tagSeparator.charCodeAt(0) || event.which === 13) {
					event.preventDefault();
					fun1(event);
				}
			});
			this._tagInput.bind("keyup", {widget: this}, function(event) { // backspace needs keyup => weird but works
				// 8 === backspace
				if($(this).val() == "" && event.keyCode == 8) {
					// delete last tag
					var last = event.data.widget._tagHolder.children("li:last-child").find("span").html();
					if(last != undefined) { 
						event.data.widget.remove(last);
					}
				}
			});
			this._tagInput.bind("focusout", {widget: this}, fun1);
			this._tagInput.bind("keypress", this._inFieldResize);
			
			// refresh source input field when form is submitted
			$.each(this._submitbtns, function(index, data) {
				$(data).bind("click", {widget: self}, function(event) {
					event.data.widget.refresh();
				});
			});
			
		},

		// Use the _setOption method to respond to changes to options
		_setOption : function(key, value) {

			switch (key) {
				case "editable":
					this.options.editable = value;
					break;
				case "addOnly":
					this.options.addOnly = value;
					break;
				case "tagSeparator":
					this.options.tagSeparator = value;
					break;
				case "langPreferred":
					break;
				case "langDisabled":
						break;
			}

			// In jQuery UI 1.8, you have to manually invoke the _setOption
			// method from the base widget
			$.Widget.prototype._setOption.apply(this, arguments);
		},
		
		addItem : function(tagTxt) {
			
			if (tagTxt === undefined) { return; };
			var tagTxt = $.trim(tagTxt);
			
			var oldTag = this._getTag(tagTxt);
			if(oldTag != undefined) {
				oldTag.effect("pulsate", { times:3 }, 200);
				return;
			}
			
			var newId = "tagman-tag-" + this._elemCount;
			var newLi = $("<li id=\"" + newId + "\" class=\"wtf-tagman-tag\"></li>");
			var newTag = $("<span id=\"tagman-tag-content-" + newId + "\">" + tagTxt + "</span>");
	        var aclose = $("<a class=\"wtf-tagman-closebutton ui-icon ui-icon-circle-close\" href=\"#\"></a>");
	        
	        newLi.append(newTag);
	        if(this.options.editable && !this.options.addOnly) {
	        	newLi.append(aclose);
	        }
	        
			this._tagHolder.append(newLi);
			
			if(!this.options.addOnly) {
				aclose.bind("click", {item: newLi, widget: this }, function(event) {
					event.data.item.fadeOut("fast", function() { event.data.item.remove(); });
				});
				// make tag editable
				newTag.bind("click", { tag: newTag, num: this._elemCount, container: newLi, fun: this._editHandler, tagSeparator: this.options.tagSeparator, _inFieldResize : this._inFieldResize }, this._editHandler);
			}
			
			
			try { console.log("added tag : " + tagTxt + " with id " + newId); } catch(e) {}
			this._elemCount = this._elemCount + 1;


		},
		
		// handler for editing tags
		_editHandler : function(event) {
			var inId = "tagman-tag-edit-" + event.data.num;
			var editField = $("<input type=\"text\" size=\"15\" class=\"wtf-tagman-input\" id=\"" + inId + "\" value=\"" + event.data.tag.html() + "\">");
			
			event.data.tag.remove();
			event.data.container.prepend(editField);
			
			editField.attr("size", event.data.tag.html().length);
			editField.bind("keypress", event.data._inFieldResize);
			editField.bind("click", function(event) {
				event.stopPropagation();
			});
			
			editField.focus();
			
			// focus out AND return
			var editfun = function(event) {
				event.preventDefault();
				event.data.tag.html(editField.val());
				event.data.container.prepend(event.data.tag);
				editField.remove();
				
				event.data.tag.bind("click", { tag: event.data.tag, num: event.data.num, container: event.data.container, fun: event.data.fun, tagSeparator: event.data.tagSeparator, _inFieldResize : event.data._inFieldResize }, event.data.fun);
			};
			editField.bind("focusout", {editField: editField, tag : event.data.tag, container: event.data.container, num: event.data.num, fun: event.data.fun, tagSeparator: event.data.tagSeparator, _inFieldResize : event.data._inFieldResize}, editfun);
			editField.bind("keypress", {editField: editField, tag : event.data.tag, container: event.data.container, num: event.data.num, fun: event.data.fun, tagSeparator: event.data.tagSeparator, _inFieldResize : event.data._inFieldResize}, function(event) {
				if(event.charCode === event.data.tagSeparator.charCodeAt(0) || event.which === 13) {
					editfun(event);
				}
			});
			
			event.stopPropagation();
		},
		
		// helper function to resize input field according to its value
		_inFieldResize : function(event) {
			$(this).attr("size", $(this).val().length + 1);
		},
		
		remove : function(item) {
			var tag = this._getTag(item);
			if(tag != null) {
				tag.fadeOut("fast", function() { tag.remove(); });
			}
		},
		
		tagExists : function(tag) {
			return (this._getTag(tag) != undefined);
		},
		
		// return an array of all tags
		getTags : function() {
			var tmpA = new Array();
			$.each(this._tagHolder.find("span"), function(index, data) {
				if($.trim($(data).html()) != "") {
					tmpA.push($(data).html());
				}
			});
			
			return tmpA;
		},
		
		// returns the li element if tag exists or undefined else
		_getTag : function(tag) {
			result = undefined;
			$.each(this._tagHolder.find("span"), function(index, data) {
				if ($.trim($(data).html()) === tag) {
					result = $(data).parent("li");
				}
			});
			
			return result;
		},
		
		// adds all tags created/modified/deleted with the widget to the source input field
		refresh : function() {
						
			var s = this.getTags().join(this.options.tagSeparator + " ");
			this.element.val(s);
			try { console.log("refreshed, resulting string is: " + s); } catch(e) {}
		},

		// Use the destroy method to clean up any modifications your widget has
		// made to the DOM
		destroy : function() {

			// write tags back to the input field and remove containers
			this.refresh();			
			this.element.show();
			this._tagContainer.remove();
			
			// In jQuery UI 1.8, you must invoke the destroy method from the
			// base widget

			$.Widget.prototype.destroy.call(this);
			// In jQuery UI 1.9 and above, you would define _destroy instead of
			// destroy and not call the base method

		}

	});

}(jQuery));
